#ifndef TCPSOCKET_HPP
#define TCPSOCKET_HPP
#include <iostream>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <cstring>
#include <string>
#include "log.hpp"
#include <arpa/inet.h>
#include <netinet/in.h>

enum
{
    SOCKET_ERR = 2,
    BIND_ERR,
    LISTEN_ERR
};

class NetSocket
{
public:
    NetSocket() {}
    ~NetSocket()
    {
        if (sockfd_ >= 0)
        {
            close(sockfd_);
        }
        sockfd_ = -1;
    }

public:
    void Socket()
    {
        sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd_ < 0)
        {
            lg(FATAL, "socket fail, errno: %d, error message:%s", errno, strerror(errno));
            exit(SOCKET_ERR);
        }
        lg(INFO, "socket sucess ...");
        int opt = 1;
        setsockopt(sockfd_,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt));
        
    }

    void Bind(const uint16_t port)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_port = htons(port);

        socklen_t len = sizeof(local);

        if (bind(sockfd_, (struct sockaddr *)&local, len) < 0)
        {
            lg(FATAL, "bind fail, errno: %d, error message:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(INFO, "bind sucess ...");
    }

    void Listen(int backlog = 7)
    {
        if (listen(sockfd_, backlog) < 0)
        {
            lg(FATAL, "listen fail, errno: %d, error message:%s", errno, strerror(errno));
            exit(LISTEN_ERR);
        }
        lg(INFO, "listen sucess ...");
    }

    int Accept(std::string *clientip, uint16_t *clientport)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        socklen_t len = sizeof(peer);

        int retsockfd = accept(sockfd_, (struct sockaddr *)&peer, &len);
        if (retsockfd < 0)
        {
            lg(WARNING, "accept fail, errno: %d, error message:%s", errno, strerror(errno));
            return -1;
        }
        char ipstr[32];
        inet_ntop(AF_INET, &(peer.sin_addr), ipstr, sizeof(ipstr));
        *clientip = ipstr;
        *clientport = ntohs(peer.sin_port);
        lg(INFO, "accept sucess ...");
        return retsockfd;
    }

    bool Connect(const std::string ip, const uint16_t &port)
    {
        struct sockaddr_in peer;
        memset(&peer, 0, sizeof(peer));
        peer.sin_family = AF_INET;
        peer.sin_port = htons(port);
        inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));
        socklen_t len = sizeof(peer);

        if (connect(sockfd_, (sockaddr *)&peer, len) < 0)
        {
            printf("connect fail, errno: %d, error message:%s", errno, strerror(errno));
            return false;
        }
        return true;
    }

    void Close()
    {
        if (sockfd_ >= 0)
        {
            close(sockfd_);
        }
        sockfd_ = -1;
    }

    int GetFd()
    {
        return sockfd_;
    }

private:
    int sockfd_ = -1;
};

#endif